package com.yc.springmvc.core;

import java.io.File;	
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;

import com.yc.springmvc.core.annotation.Autowired;
import com.yc.springmvc.core.annotation.Component;
import com.yc.springmvc.core.annotation.Controller;
import com.yc.springmvc.core.annotation.RequestMapping;
import com.yc.springmvc.core.util.StringUtil;


/**
 * 核心处理器
 * 1.读取配置文件获取基址路径
 * 2.扫描包获取所有的类
 * 3.初始化需要Ioc容器的类： @component @controller
 * 4.执行依赖注入 @Autowired
 * 5.构建HandlerMapper，完成URL与对应处理方法之间的映射 @RequestMapping
 * @author 谢俊峰
 * @data data
 * @QQ 1575623833
 */
public class FrameworkCore {
	private String contextConfigLocation;
	private String basePackage;
	private Set<String> className = new CopyOnWriteArraySet<String>();  // 存储扫描类路径得到的类信息
	private Map<String, Object> instanceObject = new Hashtable<String, Object>(); //  Ioc容器
	private Map<String, HandlerMapperInfo> handlerMappers = new Hashtable<String, HandlerMapperInfo>();

	public FrameworkCore(String contextConfigLocation) {
		this.contextConfigLocation = contextConfigLocation;
		init();
	}

	/**
	 * 初始化方法
	 */
	private void init() {
		// TODO 1.读取配置文件获取基址路径
		doLoadConfig();
//		System.out.println("basePackage" + "--" + basePackage); 
		
		// TODO 2.扫描包获取所有的类
		doScannerPackage();
//		className.forEach(System.out::println);
		
		// TODO 3.初始化需要Ioc容器的类： @component @controller
		doInstanceObject();
//		instanceObject.forEach((key, val) -> {
//			System.out.println(key + ":" + val);
//		});
		
		// TODO 4.执行依赖注入 @Autowired
		try {
			doAutowired();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		}
		
		// TODO 5.构建HandlerMapper，完成URL与对应处理方法之间的映射 @RequestMapping
		initHandlerMapping();
//		handlerMappers.forEach((key, val) ->{
//			System.out.println(key + ":" + val);
//		});
		
	}

	// 读取配置文件获取基址路径
	private void doLoadConfig() {
		try (InputStream is = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation)){
			Properties pro = new Properties();
			pro.load(is);
			basePackage = pro.getProperty("basepackage");
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	// 扫描包获取所有的类
	private void doScannerPackage() {
		if (StringUtil.checkNull(basePackage)) {
			throw new RuntimeException("读取配置文件失败，请配置contextConfigLocation参数以及basepackage属性...");
		}
		
		URL url = this.getClass().getClassLoader().getResource(basePackage.replaceAll("\\.", "/"));
//		System.out.println("url: " + url);
		
		File fl = new File(url.getFile());
		
		getClassInfo(basePackage, fl);
	}

	private void getClassInfo(String basePackage, File fl) {
		if (fl.exists() && fl.isDirectory()) {
			for (File f : fl.listFiles()) {
				if (f.isDirectory()) {  // 说明是目录
					getClassInfo(basePackage + "." + f.getName(), f);
					continue;
				}
				className.add(basePackage + "." + f.getName().replace(".class", ""));
			}
		}
	}

	// 初始化需要Ioc容器的类： @component @controller
	private void doInstanceObject() {
		if (className.isEmpty()) {
			return;
		}
		
		Class<?> cls = null;
		String beanName = null;
		String name = null;
		
		for (String className : className) {
			try {
				cls = Class.forName(className);  // 加载类
				if (cls.isAnnotationPresent(Controller.class)) {
					beanName = cls.getAnnotation(Controller.class).value();
					
					if (StringUtil.checkNull(beanName)) {  // 说明用户没有指定名称 用类名首字母小写
						name = cls.getSimpleName();
						beanName = name.substring(0, 1).toLowerCase() + name.substring(1);
 					}
					
					instanceObject.put(beanName, cls.newInstance());
					continue;
				}
				
				if (cls.isAnnotationPresent(Component.class)) {
					beanName = cls.getAnnotation(Component.class).value();
					
					if (StringUtil.checkNull(beanName)) {  // 说明用户没有指定名称 用类名首字母小写
						name = cls.getSimpleName();
						beanName = name.substring(0, 1).toLowerCase() + name.substring(1);
 					}
					
					instanceObject.put(beanName, cls.newInstance());
					continue;
				}
			} catch (ClassNotFoundException e) {
				e.printStackTrace();
			} catch (InstantiationException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			}
		}
	}

	// 执行依赖注入 @Autowired
	private void doAutowired() throws IllegalArgumentException, IllegalAccessException {
		if (instanceObject.isEmpty()) {
			return;
		}
		
		Class<?> cls = null;
		Field[] fields = null;
		Autowired awd = null;
		List<Object> list = new ArrayList<Object>();
		String beanName = null;
		Object valObj = null;
		Class<?> typeCls = null;
		Collection<Object> objs = instanceObject.values(); // 获取Ioc容器的所有对象
		
		for (Entry<String, Object> entry : instanceObject.entrySet()) {
			cls = entry.getValue().getClass();
			
			fields = cls.getDeclaredFields();  // 获取这个类中的所有属性
			if (fields == null || fields.length <= 0) {
				continue;
			}
				
			for (Field fd : fields) {
				list.clear();
				if (!fd.isAnnotationPresent(Autowired.class)) {  // 说明不需要注值
					continue;
				}
				
				fd.setAccessible(true);  // 强吻
				
				awd = fd.getAnnotation(Autowired.class);
				beanName = awd.value();
				if (!StringUtil.checkNull(beanName)) {  // 说明用户指定了根据名称注值
					valObj = instanceObject.get(beanName);
					if (valObj == null) {
						throw new RuntimeException(cls.getName() + "." + fd.getName() + "注值失败，没有对应的实体类对象...");
					}
					
					fd.set(entry.getValue(), valObj);
					continue;
				}
				
				typeCls = fd.getType();  // 获取这个属性的类型
				
				for (Object obj : objs) {
					if (obj.getClass() == typeCls) {
						list.add(obj);
					} else {
						for (Class<?> c : obj.getClass().getInterfaces()) {
							if (c == typeCls) {
								list.add(obj);
							}
						}
					}
				}
				
				if (list.isEmpty()) {
					throw new RuntimeException(cls.getName() + "." + fd.getName() + "注值失败，没有对应的实体类对象...");
				}
				
				if (list.size() > 1) {
					fd.set(entry.getValue(), list.get(0));
					continue;
				}
				
				if (list.size() > 1) {
					beanName = fd.getName();
					valObj = instanceObject.get(beanName);
					if (valObj == null) {
						throw new RuntimeException(cls.getName() + "." + fd.getName() + "注值失败，有多个相同类型的对象:" + list + "，没有对应的benaName...");
					}
					fd.set(entry.getValue(), valObj);
				}
			}
		}
		
	}

	// 构建HandlerMapper，完成URL与对应处理方法之间的映射 @RequestMapping
	private void initHandlerMapping() {
		if (instanceObject.isEmpty()) {
			return;
		}
		
		Method[] methods = null;
		Class<?> cls = null;
		RequestMapping requestMapping = null;
		String baseUrl = "";
		String url = null;
		
		for (Entry<String, Object> entry : instanceObject.entrySet()) {
			cls = entry.getValue().getClass();
			
			if (!cls.isAnnotationPresent(Controller.class)) {  // 说明不是控制层
				continue;
			}
			
			requestMapping = cls.getAnnotation(RequestMapping.class);
			if (requestMapping != null) {
				baseUrl = requestMapping.value();  // 获取配置在类上的请求路径
				
				if (!baseUrl.startsWith("/")) {
					baseUrl = "/" + baseUrl;
				}
			}
			
			methods = cls.getDeclaredMethods();
			if (methods == null || methods.length <= 0) {
				continue;
			}
			
			for (Method method : methods) {
				if (!method.isAnnotationPresent(RequestMapping.class)) {
					continue;
				}
				
				requestMapping = method.getAnnotation(RequestMapping.class);
				url = requestMapping.value();
				if (!url.startsWith("/")) {
					url = "/" + url;
				}
				
				url = baseUrl + url;
				handlerMappers.put(url.replaceAll("/+", "/"), new HandlerMapperInfo(method, entry.getValue()));
			}
		}
	}

	public HandlerMapperInfo getMapper(String url) {
		return handlerMappers.getOrDefault(url, null);  // 如果有对应的键就返回对应的值 否则返回空
	}
}
